package upb.webserver.generate.code;

import com.upb.webserver.common.enums.RegexPattern;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.returncode.RetCode;
import com.upb.webserver.common.tools.WebServerTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
public class ConstantCodeTools {
    //Change according to actual situation
    public static File TARGET_FILE;
    private static final Class TARGET_FILE_CLASS = ConstantCode.class;

    //constant
    private static String MODULE_START_FORMAT = "    //[%1s][start]";
    private static String MODULE_END_FORMAT = "    //[%1s][end]";
    private static String RETURN_CODE_FORMAT = "    public static final RetCode %1s = RetCode.mark(%2d, \"%3s\");";

    private static Map<Integer, String> codeMsgMap = new ConcurrentHashMap<>();
    private static Set<String> returnCodeNameSet = new HashSet<>();
    private static Integer maxCode;
    private static List<String> lineAll;

    static {
        String compilePath = ConstantCode.class.getClassLoader().getResource(".").getPath();
        String basePath = compilePath.substring(0, compilePath.indexOf("build"));
        File baseFile = new File(basePath);
        List<File> fileList = WebServerTools.listFileByName(baseFile, String.format("%1s.java", ConstantCode.class.getSimpleName()));
        TARGET_FILE = fileList.get(0);
    }

    /**
     * init return code map
     *
     * @throws IllegalAccessException
     */
    private static void initReturnCodeMap() throws IllegalAccessException {
        Field[] fields = TARGET_FILE_CLASS.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.getType() == RetCode.class && Modifier.isStatic(field.getModifiers())) {
                RetCode retCode = (RetCode) field.get(TARGET_FILE_CLASS);
                codeMsgMap.put(retCode.getCode(), retCode.getMessage());
                returnCodeNameSet.add(field.getName());
            }
        }
    }


    /**
     * check param.
     *
     * @param name
     */
    private static void checkParam(String name, String message) {
        log.info("start exec method [checkNewRetCode]. name:{} message:{}", name, message);

        if (returnCodeNameSet.contains(name)) {
            throw new RuntimeException(String.format("fail exec method [checkNewRetCode].found name[%1s]", name));
        }

        if (!WebServerTools.matcher(RegexPattern.SIMPLE_STRING1, name)) {
            log.warn("name not match:{}", name);
            throw new RuntimeException(String.format("fail exec method [writeLine]. name not match:%1s", name));
        }

        if (codeMsgMap.containsValue(message)) {
            throw new RuntimeException(String.format("fail exec method [checkNewRetCode].found message[%1s]", message));
        }
    }


    /**
     * @param module
     * @return
     */
    private static Integer getNextCode(ModuleEnum module) {
        int nextCode = maxCode + 1;
        if (module.getMinCode() != null) {
            nextCode = module.getMinCode() + 1;
        }

        while (codeMsgMap.containsKey(nextCode)) {
            nextCode++;
        }
        return nextCode;
    }


    /**
     *
     */
    private static void readTargetFile() {
        try {
            lineAll = Files.readAllLines(Paths.get(TARGET_FILE.toURI()));

            initReturnCodeMap();

            maxCode = codeMsgMap.keySet().stream().max(Comparator.comparing(Function.identity())).orElse(0);
        } catch (IOException | IllegalAccessException e) {
            log.error("param init not success:{}", TARGET_FILE.toURI().toString());
            System.exit(0);
        }
    }


    /**
     * @param module
     * @param name
     * @param message
     */
    public static void writeLine(ModuleEnum module, String name, String message) {
        log.info("start exec method [writeLine]. name:{} message:{}", name, message);

        //read content of target file
        readTargetFile();

        name = name.toUpperCase();
        //check param
        checkParam(name, message);

        int nextCode = getNextCode(module);
        String newLine = String.format(RETURN_CODE_FORMAT, name, nextCode, message);
        String startFlag = String.format(MODULE_START_FORMAT, module.getValue());
        String endFlag = String.format(MODULE_END_FORMAT, module.getValue());

        log.info(">startFlag:{}", startFlag);
        log.info(">newLine:{}", newLine);
        log.info(">endFlag:{}", endFlag);

        try {
            List<String> allLineNoBlank = lineAll.stream().map(l -> l.trim()).collect(Collectors.toList());
            int endFlagIndex = allLineNoBlank.indexOf(endFlag.trim());
            if (endFlagIndex>0) {
                lineAll.add(endFlagIndex, newLine);
            } else {
                lineAll.add(lineAll.size() - 1, "");
                lineAll.add(lineAll.size() - 1, startFlag);
                lineAll.add(lineAll.size() - 1, newLine);
                lineAll.add(lineAll.size() - 1, endFlag);
            }

            FileUtils.writeLines(TARGET_FILE, lineAll, false);
            log.info("success exec method [writeLine]. ");
            System.out.println(">>>> ");
            System.out.println(String.format(">>>> %1s.%2s", TARGET_FILE_CLASS.getSimpleName(), name));
            System.out.println(">>>> ");
        } catch (Exception ex) {
            log.error("fail exec method [writeLine]. ", ex);
            System.exit(0);
        }
    }



    public static void writeCheckCodeLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.CHECK_CODE, name, message);
    }

    public static void writeOrgLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.ORG, name, message);
    }

    public static void writeRestLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.REST, name, message);
    }

    public static void writeWorkOrderLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.WORK_ORDER, name, message);
    }

    public static void writeSecurityAuthLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.SECURITY_AUTH, name, message);
    }

    public static void writeTokenLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.TOKEN, name, message);
    }

    public static void writeAccountLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.ACCOUNT, name, message);
    }

    public static void writeChainLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.CHAIN, name, message);
    }

    public static void writeApplicationLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.APPLICATION, name, message);
    }

    public static void writeContractLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.CONTRACT, name, message);
    }

    public static void writeFiscoChainLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.CHAIN, name, message);
    }

//    public static void writeFiscoApplicationLine(String name, String message) {
//        ConstantCodeTools.writeLine(ModuleEnum.FISCO_APPLICATION, name, message);
//    }

    public static void writeUserLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.USER, name, message);
    }

    public static void writeConfigLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.CONFIG, name, message);
    }

    public static void writeHostLine(String name, String message) {
        ConstantCodeTools.writeLine(ModuleEnum.HOST, name, message);
    }



    public static void main(String args[]) throws IOException {
        System.out.println("name----" + TARGET_FILE_CLASS.getCanonicalName());

//        ConstantCodeTools.writeCheckCodeLine("INVALID_CHECK_CODE", "invalid checkCode");
//        ConstantCodeTools.writeSecurityAuthLine("INVALID_TOKEN", "access denied1");
//        ConstantCodeTools.writeWorkOrderLine("INVALID_WORK_ORDER_ID1", "invalid work order id1");
//        ConstantCodeTools.writeRestLine("REQUEST_STAT_HTTP_ERROR1", "request Stat fail, please check Stat1");
//        ConstantCodeTools.writeTokenLine("TOKEN_EXPIRE", "token expire");
//        ConstantCodeTools.writeDeveloperLine("invalid_developer_id", "invalid developer id");
//        ConstantCodeTools.writeOrgLine("INVALID_MESSAGE_STATUS", "invalid message status");
//        ConstantCodeTools.writeCheckCodeLine("INVALID_CHECK_CODE", "invalid checkCode");


    }
}
